/**
 * Project Name:payment
 * File Name:SignUtils.java
 * Package Name:cn.swiftpass.utils.payment.sign
 * Date:2014-6-27下午3:22:33
 */

package com.acooly.module.openapi.client.provider.baofup.utils;

import com.acooly.core.common.exception.BusinessException;
import com.acooly.core.utils.Strings;
import com.acooly.core.utils.security.RSA;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.*;


/**
 * ClassName:SignUtils
 * Function: 签名用的工具箱
 * Date:     2014-6-27 下午3:22:33
 *
 * @author
 */
@Slf4j
public class SignUtils {

    public final static String RSA_CHIPER = "RSA/ECB/PKCS1Padding";

    /**
     * 编码
     */
    public final static String ENCODE = "UTF-8";

    /**
     * 1024bit 加密块 大小
     */
    public final static int ENCRYPT_KEYSIZE = 117;
    /**
     * 1024bit 解密块 大小
     */
    public final static int DECRYPT_KEYSIZE = 128;


    /**
     * 过滤参数
     *
     * @param sArray
     * @return
     * @author
     */
    public static Map<String, String> paraFilter(Map<String, String> sArray) {
        Map<String, String> result = new HashMap<String, String>(sArray.size());
        if (sArray == null || sArray.size() <= 0) {
            return result;
        }
        for (String key : sArray.keySet()) {
            String value = sArray.get(key);
            if (value == null || value.equals("") || key.equalsIgnoreCase("sign")) {
                continue;
            }
            result.put(key, value);
        }
        return result;
    }

    /**
     * <一句话功能简述>
     * <功能详细描述>将map转成String
     *
     * @param payParams
     * @return
     * @see [类、类#方法、类#成员]
     */
    public static String payParamsToString(Map<String, String> payParams) {
        return payParamsToString(payParams, false);
    }

    public static String payParamsToString(Map<String, String> payParams, boolean encoding) {
        return payParamsToString(new StringBuilder(), payParams, encoding);
    }

    /**
     * @param payParams
     * @return
     * @author
     */
    public static String payParamsToString(StringBuilder sb, Map<String, String> payParams, boolean encoding) {
        buildPayParams(sb, payParams, encoding);
        return sb.toString();
    }

    /**
     * @param payParams
     * @return
     * @author
     */
    public static void buildPayParams(StringBuilder sb, Map<String, String> payParams, boolean encoding) {
        List<String> keys = new ArrayList<String>(payParams.keySet());
        Collections.sort(keys);
        for (String key : keys) {
            sb.append(key).append("=");
            if (encoding) {
                sb.append(urlEncode(payParams.get(key)));
            } else {
                sb.append(payParams.get(key));
            }
            sb.append("&");
        }
        sb.setLength(sb.length() - 1);
    }

    /**
     * @param payParams
     * @return
     * @author
     */
    public static String encodeBizContent(String payParams) {
        LinkedHashMap<String, String> jsonMap = JSON.parseObject(payParams, new TypeReference<LinkedHashMap<String, String>>() {
        });
        JSONObject jsonObject = new JSONObject(true);
        for (Map.Entry<String, String> entry : jsonMap.entrySet()) {
            jsonObject.put(entry.getKey(),urlEncode(entry.getValue()));
        }
        return jsonObject.toJSONString();
    }

    public static String urlEncode(String str) {
        try {
            return URLEncoder.encode(str, "UTF-8");
        } catch (Throwable e) {
            return str;
        }
    }

    /**
     * 遍历以及根据重新排序
     *
     * @param sortedParams
     * @return
     */
    public static String getSignContent(Map<String, String> sortedParams) {
        StringBuffer content = new StringBuffer();
        List<String> keys = new ArrayList<String>(sortedParams.keySet());
        Collections.sort(keys);
        int index = 0;
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = sortedParams.get(key);
            if (Strings.isNotBlank(value)) {
                content.append((index == 0 ? "" : "&") + key + "=" + value);
                index++;
            }
        }
        return content.toString();
    }

    /**
     * 宝付RSA私钥验签
     *
     * @param signStr
     * @param keystoreUri
     * @param keystoreType
     * @param keystorePassword
     * @return
     */
    public static String bfSignRSA(String signStr, String keystoreUri, String keystoreType, String keystorePassword) {
        PrivateKey privateKey = RSA.loadPrivateKeyFromKeyStore(keystoreUri, keystoreType,
                keystorePassword);
        return encryptByPrivateKey(signStr, privateKey);
    }

    /**
     * 宝付RSA私钥验签
     *
     * @param signStr
     * @param keystoreUri
     * @return
     */
    public static String bfVerifySignRSA(String signStr, String keystoreUri) {
        PublicKey publicKey = RSA.loadPublicKeyFromCert(keystoreUri);
        return decryptByPublicKey(signStr, publicKey);
    }

    /**
     * 根据私钥加密
     *
     * @param src
     * @param privateKey
     */
    public static String encryptByPrivateKey(String src, PrivateKey privateKey) {

        byte[] destBytes = rsaByPrivateKey(src.getBytes(), privateKey, Cipher.ENCRYPT_MODE);

        if (destBytes == null) {
            throw new BusinessException("私钥签名失败");
        }
        return StringHelper.byte2Hex(destBytes);

    }

    /**
     * 根据公钥解密
     *
     * @param src
     * @param publicKey
     * @return
     */
    public static String decryptByPublicKey(String src, PublicKey publicKey) {

        try {
            byte[] destBytes = rsaByPublicKey(StringHelper.hex2Bytes(src), publicKey, Cipher.DECRYPT_MODE);

            if (destBytes == null) {
                throw new BusinessException("公钥验签失败");
            }
            return new String(destBytes, ENCODE);
        } catch (UnsupportedEncodingException e) {
            log.error("解密内容不是正确的UTF8格式:", e);
        }
        return null;
    }

    /**
     * 私钥算法
     *
     * @param srcData    源字节
     * @param privateKey 私钥
     * @param mode       加密 OR 解密
     * @return
     */
    public static byte[] rsaByPrivateKey(byte[] srcData, PrivateKey privateKey, int mode) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_CHIPER);
            cipher.init(mode, privateKey);
            // 分段加密
            int blockSize = (mode == Cipher.ENCRYPT_MODE) ? ENCRYPT_KEYSIZE : DECRYPT_KEYSIZE;
            byte[] decryptData = null;
            for (int i = 0; i < srcData.length; i += blockSize) {
                byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(srcData, i, i + blockSize));
                decryptData = ArrayUtils.addAll(decryptData, doFinal);
            }
            return decryptData;
        } catch (NoSuchAlgorithmException e) {
            log.error("私钥算法-不存在的解密算法:", e);
        } catch (NoSuchPaddingException e) {
            log.error("私钥算法-无效的补位算法:", e);
        } catch (IllegalBlockSizeException e) {
            log.error("私钥算法-无效的块大小:", e);
        } catch (BadPaddingException e) {
            log.error("私钥算法-补位算法异常:", e);
        } catch (InvalidKeyException e) {
            log.error("私钥算法-无效的私钥:", e);
        }
        return null;
    }

    /**
     * 公钥算法
     *
     * @param srcData   源字节
     * @param publicKey 公钥
     * @param mode      加密 OR 解密
     * @return
     */
    public static byte[] rsaByPublicKey(byte[] srcData, PublicKey publicKey, int mode) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_CHIPER);
            cipher.init(mode, publicKey);
            // 分段加密
            int blockSize = (mode == Cipher.ENCRYPT_MODE) ? ENCRYPT_KEYSIZE : DECRYPT_KEYSIZE;
            byte[] encryptedData = null;
            for (int i = 0; i < srcData.length; i += blockSize) {
                // 注意要使用2的倍数，否则会出现加密后的内容再解密时为乱码
                byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(srcData, i, i + blockSize));
                encryptedData = ArrayUtils.addAll(encryptedData, doFinal);
            }
            return encryptedData;
        } catch (NoSuchAlgorithmException e) {
            log.error("公钥算法-不存在的解密算法:", e);
        } catch (NoSuchPaddingException e) {
            log.error("公钥算法-无效的补位算法:", e);
        } catch (IllegalBlockSizeException e) {
            log.error("公钥算法-无效的块大小:", e);
        } catch (BadPaddingException e) {
            log.error("公钥算法-补位算法异常:", e);
        } catch (InvalidKeyException e) {
            log.error("公钥算法-无效的私钥:", e);
        }
        return null;
    }

    /**
     * 获取报文中<Signature></Signature>部分
     *
     * @param xml
     * @return
     */
    public static String getSign(String xml) {
        int s_index = xml.indexOf("<Signature>");
        int e_index = xml.indexOf("</Signature>");
        String sign = null;
        if (s_index > 0) {
            sign = xml.substring(s_index + 11, e_index);
        }
        return sign;
    }


    /**
     * 获取body部分
     *
     * @param xml
     * @return
     */
    public static String getBodyXml(String xml, String status) {
        int s_index = -1;
        int e_index = -1;
        if ("hxWithdraw".equals(status)) {
            s_index = xml.indexOf("<Body>");
            e_index = xml.indexOf("</Body>");
        } else {
            s_index = xml.indexOf("<body>");
            e_index = xml.indexOf("</body>");
        }

        String sign = null;
        if (s_index > 0) {
            sign = xml.substring(s_index, e_index + 7);
        }
        return sign;
    }

    /**
     * 获取body部分
     *
     * @param xml
     * @return
     */
    public static String getBodyXml(String xml) {
        int s_index = xml.indexOf("<body>");
        int e_index = xml.indexOf("</body>");
        String sign = null;
        if (s_index > 0) {
            sign = xml.substring(s_index, e_index + 7);
        }
        return sign;
    }

    /**
     * 获取报文中<RspCode></RspCode>部分
     *
     * @param xml
     * @return
     */
    public static String getRspCode(String xml) {
        int s_index = xml.indexOf("<RspCode>");
        int e_index = xml.indexOf("</RspCode>");
        String sign = null;
        if (s_index > 0) {
            sign = xml.substring(s_index + 9, e_index);
        }
        return sign;
    }

    /**
     * 获取报文中<Status></Status>部分
     *
     * @param xml
     * @return
     */
    public static String getStatus(String xml) {
        int s_index = xml.indexOf("<Status>");
        int e_index = xml.indexOf("</Status>");
        String sign = null;
        if (s_index > 0) {
            sign = xml.substring(s_index + 8, e_index);
        }
        return sign;
    }

    /**
     * 获取报文中<RetEncodeType></RetEncodeType>部分
     *
     * @param xml
     * @return
     */
    public static String getRetEncodeType(String xml) {
        int s_index = xml.indexOf("<RetEncodeType>");
        int e_index = xml.indexOf("</RetEncodeType>");
        String sign = null;
        if (s_index > 0) {
            sign = xml.substring(s_index + 15, e_index);
        }
        return sign;
    }

    /**
     * 获取报文中<Amount></Amount>部分
     *
     * @param xml
     * @return
     */
    public static String getAmount(String xml) {
        int s_index = xml.indexOf("<Amount>");
        int e_index = xml.indexOf("</Amount>");
        String sign = null;
        if (s_index > 0) {
            sign = xml.substring(s_index + 8, e_index);
        }
        return sign;
    }

    /**
     * 获取报文中<Date></Date>部分
     *
     * @param xml
     * @return
     */
    public static String getDate(String xml) {
        int s_index = xml.indexOf("<Date>");
        int e_index = xml.indexOf("</Date>");
        String sign = null;
        if (s_index > 0) {
            sign = xml.substring(s_index + 6, e_index);
        }
        return sign;
    }

    /**
     * 获取配置文件
     *
     * @return
     */
    public Map<String, String> getParamsByCfg() {
        try {
            Properties prop = new Properties();
            InputStream ins = this.getClass().getClassLoader().getResourceAsStream("configurations.properties");
            prop.load(ins);
            // 商户号(需与页面上传输的商户号保持一致)
            String merCode = prop.getProperty("merCode");
            // 商户证书
            String directStr = prop.getProperty("directStr");
            // ips公钥
            String ipsRsaPub = prop.getProperty("ipsRsaPub");
            //String ipsRsaPub = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCbfyYdw2j5gOF7X9cdFrUKJ+MR\nTAfpJB+opBxjSw7iAZNUv9TmQHH/LSAim2ucaBRiB/Cqm1agocip3g8YC7Md/AhC\ntN+di0uc3d0F2c7H/WZm4n98IPjwfjmxNUJxdvKnF3CezY9nCCHWu36NvtMlCKLl\nO14Iu/PNvsVVv85zowIDAQAB\n-----END PUBLIC KEY-----";
            System.out.println("merCode ：" + merCode);
            System.out.println("directStr ：" + directStr);
            System.out.println("ipsRsaPub ：" + ipsRsaPub);
            Map<String, String> map = new HashMap<String, String>();
            map.put("merCode", merCode);
            map.put("directStr", directStr);
            map.put("ipsRsaPub", ipsRsaPub);
            return map;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


}

